package com.cloud.aop;

import com.alibaba.fastjson.JSON;
import com.cloud.annotation.CloudLimitAnnotation;
import com.cloud.rest.RestRes;
import com.google.common.util.concurrent.RateLimiter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Component
@Aspect
public class CloudLimitAop {

    //多线程-线程共享
    private ConcurrentHashMap<String, RateLimiter> rateLimiters = new ConcurrentHashMap();

    @Value("${cloud.joyoung.limit:1000}")
    private String limitCount;

    @Around(value = "@annotation(com.joyoung.logrecord.annotation.CloudLimitAnnotation)")
    public Object around(ProceedingJoinPoint joinPoint) {

        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        //获取请求路径，作为key
        String requestURI = request.getRequestURI();
        //可以使用限流
        try {
            //获取拦截的方法名称
            Signature sig = joinPoint.getSignature();
            MethodSignature methodSignature = (MethodSignature) sig;
            //获取方法上的LimitInterface注解
            CloudLimitAnnotation cloudLimitAnnotation = methodSignature.getMethod().getDeclaredAnnotation(CloudLimitAnnotation.class);


            Double token = Double.parseDouble(limitCount);
            //获取到注解的token；
            if (StringUtils.isNotBlank(cloudLimitAnnotation.token())) {
                token = Double.parseDouble(cloudLimitAnnotation.token());
            }
            RateLimiter rateLimiter = rateLimiters.get(requestURI);
            if (rateLimiter == null) {
                rateLimiter = RateLimiter.create(token);
                rateLimiters.put(requestURI, rateLimiter);
            }

            //限流的使用
            boolean result = rateLimiter.tryAcquire();
            if (!result) {
                log.info(getParam(joinPoint, request));
                return RestRes.errorData("500", "此路已经被限流,限流：" + limitCount);
            }

            Object proceed = joinPoint.proceed();
            //proceed就是方法的返回值
            return proceed;
        } catch (Throwable e) {
            return RestRes.errorData("500", e.getMessage());
        }
    }

    private String getParam(ProceedingJoinPoint joinPoint, HttpServletRequest request) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        //请求参数
        String contentTypes = request.getContentType();
        HashMap<Object, Object> reqParams = new HashMap<>();

        //json 数据
        if (StringUtils.isNotBlank(contentTypes) && MediaType.APPLICATION_JSON_VALUE.contains(contentTypes)) {
            if (("POST".equals(request.getMethod()) || "PUT".equals(request.getMethod()) || "PATCH".equals(request.getMethod()) || "DELETE".equals(request.getMethod()))) {
                LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
                String[] paramNames = u.getParameterNames(method);
                //方法 1 请求的方法参数值 JSON 格式 null不显示
                if (joinPoint.getArgs().length > 0) {
                    Object[] args = joinPoint.getArgs();
                    for (int i = 0; i < args.length; i++) {
                        //请求参数类型判断过滤，防止JSON转换报错
                        if (args[i] instanceof HttpServletRequest || args[i] instanceof HttpServletResponse) {
                            continue;
                        }
                        if (args[i] instanceof MultipartFile) {
                            MultipartFile arg = (MultipartFile) (args[i]);
                            String originalFilename = arg.getOriginalFilename();
                            log.info(originalFilename);
                            reqParams.put(paramNames[i], originalFilename);
                            continue;
                        }
                        log.info("请求参数名称 :" + paramNames[i] + ", 内容 :" + JSON.toJSONString(args[i]));
                        reqParams.put(paramNames[i], JSON.toJSONString(args[i]));
//                        param.put(paramNames[i], args[i]);
                    }
                }
            }
        }
        return JSON.toJSONString(reqParams);
    }
}
